/*
 * Copyright Iain Sproat, 2008
 * 
 *
 */

using System;
using NUnit.Framework;


using FreeBase.Fluent;
using FreeBase.Fluent.MQLConverter;

namespace FreeBase_Test.Linq
{
	/// <summary>
	/// These are not strictly unit tests
	/// but are recreations of some of the examples from the MQL Reference Guide.
	/// These should be a good reference of how to use the Fluent extension methods
	/// </summary>
	[TestFixture]
	public class ExamplesFromReferenceGuide
	{
		IMQLConverter SUT = null;
		string expected = string.Empty;
		FBQuery query = null;
		string returned = string.Empty;
		bool testWasRun; //a flag which helps me from forgetting to write RunTest at the end of a test method!
		
		[SetUp]
		public void SetUp()
		{
			expected = string.Empty;
			returned = string.Empty;
			testWasRun = false;
			SUT = new FluentMQLConverter();
		}
		
		[Test]
		public void Example_3_2_1()
		{
			query = Freebase.Query
				.Where().Name.Is("The Police")
				.Where().Type.Is("/music/artist")
				.Show.ManyRecordsFor.Property("album");
			
			expected = "{\"query\":[{\"album\":[]," +
				"\"name\":\"The Police\"," +
				"\"type\":\"/music/artist\"}]}";
			RunTest();
		}
		
		[Test]
		public void Example_3_2_3()
		{
			query = Freebase.Query
				.Show.OneRecordFor.Id
				.Where().Type.Is("/music/artist")
				.Where().Name.Is("The Police");
			
			
			expected = "{\"query\":[{" +
				"\"id\":null," +
				"\"type\":\"/music/artist\"," +
				"\"name\":\"The Police\"" +
				"}]}";
			RunTest();
		}
		
		[Test]
		public void Example_3_2_4()
		{
			query = Freebase.Query
				.Show.ManyRecordsFor.Type
				.Show.OneRecordFor.Name
				.Where().Id.Is("/en/the_police");
			
			expected = "{\"query\":[{" +
				"\"type\":[]," +
				"\"name\":null," +
				"\"id\":\"/en/the_police\"}]}";
			RunTest();
		}
		
		[Test]
		public void Example_3_2_5()
		{
			query = Freebase.Query
				.Where().Type.Is("/music/artist")
				.Where().Name.Is("The Police")
				.Expect.One("album").Which.Meets(
					Freebase.InnerQuery
					.Where().Name.Is("Synchronicity")
					.Show.ManyRecordsFor.Property("track")
				);
			
			expected = "{\"query\":[{" +
				"\"type\":\"/music/artist\"," +
				"\"name\":\"The Police\"," +
				"\"album\":{" +
				"\"track\":[]," +
				"\"name\":\"Synchronicity\"" +
				"}}]}";
			RunTest();
		}
		
		[Test]
		public void Example_3_2_5_no2()
		{
			expected = "{\"query\":[{" +
				"\"name\":null," +
				"\"type\":\"/music/artist\"," +
				"\"album\":[{" +
				"\"name\":null," +
				"\"track\":[{" +
				"\"length\":null," +
				"\"name\":\"Too Much Information\"" +
				"}]" +
				"}]" +
				"}]}";
			
			
			query = Freebase.Query
				.Show.OneRecordFor.Name
				.Where().Type.Is("/music/artist")
				.Expect.Many("album").Which.Meet(
					Freebase.InnerQuery
					.Show.OneRecordFor.Name
					.Expect.Many("track").Which.Meet(
						Freebase.InnerQuery
						.Show.OneRecordFor.Property( "length" )
						.Where().Name.Is( "Too Much Information" )
					)
				);
			RunTest();
		}
		
		[Test]
		public void Example_3_2_7()
		{
			expected = "{\"query\":[{" +
				"\"name\":{}," +
				"\"type\":[{}]," +
				"\"id\":\"/en/the_police\"" +
				"}]}";
			
			query = Freebase.Query
				.Where().Id.Is("/en/the_police")
				.Show.OneObjectFor.Name
				.Show.ManyObjectsFor.Type;
			
			RunTest();
		}
		
		[Test]
		public void Example_3_2_8()
		{
			expected = "{\"query\":[{" +
				"\"track\":[]," +
				"\"type\":\"/music/album\"," +
				"\"name\":{\"value\":\"Synchronicity\",\"lang\":\"/lang/en\"}," +
				"\"artist\":{\"name\":\"The Police\"}" +
				"}]}";
			
			query = Freebase.Query
				.Where().Type.Is("/music/album")
				.Expect.One("name").Which.Meets( Freebase.InnerQuery
				                                .Where().Value.Is("Synchronicity")
				                                .Where().Language.Is("/lang/en"))
				.Expect.One("artist").Which.Meets( Freebase.InnerQuery
				                                  .Where().Name.Is("The Police"))
				.Show.ManyRecordsFor.Property("track");
			RunTest();
		}
		
		[Test]
		public void Example_3_4_1()
		{
			expected = "{\"query\":[{" +
				"\"/common/topic/image\":[{}]," +
				"\"type\":\"/music/artist\"," +
				"\"name\":\"The Police\"," +
				"\"album\":[{" +
				"\"name\":null," +
				"\"track\":[]" +
				"}]}]}";
			
			query = Freebase.Query
				.Show.ManyObjectsFor.Property("/common/topic/image")
				.Where().Type.Is("/music/artist")
				.Where().Name.Is("The Police")
				.Expect.Many("album").Which.Meet(
					Freebase.InnerQuery
					.Show.Name
					.Show.ManyRecordsFor.Property("track")
				);
			RunTest();
		}
		
		[Test]
		public void Example_3_4_2()
		{
			expected = "{\"query\":[{" +
				"\"name\":null," +
				"\"type\":\"/music/artist\"," +
				"\"criteria0:album\":\"Greatest Hits\"," +
				"\"criteria1:album\":\"Super Hits\"" +
				"}]}";
			query = Freebase.Query
				.Show.Name
				.Where().Type.Is("/music/artist")
				.Where("album").Is("Greatest Hits")
				.Where("album").Is("Super Hits");
			
			RunTest();
		}
		
		[Test]
		public void Example_3_4_2_no2()
		{
			expected = "{\"query\":[{" +
				"\"name\":null," +
				"\"album\":[]," +
				"\"criteria0:type\":\"/music/artist\"," +
				"\"criteria1:type\":\"/people/person\"," +
				"\"criteria0:album\":\"Greatest Hits\"," +
				"\"criteria1:album\":\"Super Hits\"" +
				"}]}";
			query = Freebase.Query
				.Show.Name
				.Show.ManyRecordsFor.Property( "album" )
				.Where().Type.Is("/music/artist")
				.Where().Type.Is("/people/person")
				.Where("album").Is("Greatest Hits")
				.Where("album").Is("Super Hits");
			RunTest();
		}
		
		[Test]
		public void Example_3_4_3()
		{
			expected = "{\"query\":[{" +
				"\"*\":null," +
				"\"id\":\"/guid/1f8000000002f9e349\"" +
				"}]}";
			
			query = Freebase.Query
				.Show.AllProperties
				.Where().Id.Is("/guid/1f8000000002f9e349");
			RunTest();
		}
		
		[Test]
		public void Example_3_4_4()
		{
			expected = "{\"query\":[{" +
				"\"type\" : \"/location/country\"," +
				"\"name\" : \"Monaco\"," +
				"\"!/people/person/nationality\" : []" +  //FIXME exclamation mark, !, is not good javascript
				"}]}";
			
			query = Freebase.Query
				.Show.ReciprocalOf("/people/person/nationality")
				.Where().Type.Is("/location/country")
				.Where().Name.Is("Monaco");
			
			RunTest();
		}
		
		[Test]
		public void Example_3_4_5()
		{
			expected = "{\"query\":[{" +
				"\"*\":null," +
				"\"id\":\"/music/artist/album\"," +
				"\"type\":\"/type/property\"" +
				"}]}";
			
			query = Freebase.Query
				.Show.AllProperties
				.Where().Id.Is("/music/artist/album")
				.Where().Type.IsAProperty;
			
			RunTest();
		}
		
		[Test]
		public void Example_3_4_5_3()
		{
			expected = "{\"query\":[{" +
				"\"/type/property/unique\":null," +
				"\"id\":\"/music/track/length\"" +
				"}]}";
			
			query = Freebase.Query
				.Show.IsUniqueProperty
				.Where().Id.Is("/music/track/length");
			
			RunTest();
		}
		
		[Test]
		public void Example_3_4_5_4()
		{
			expected = "{\"query\":[{" +
				"\"master_property\":null," +
				"\"reverse_property\":\"/music/artist/album\"," +
				"\"id\":\"/music/album/artist\"," +
				"\"type\":\"/type/property\"" +
				"}]}";
			query = Freebase.Query
				.Show.MasterProperty
				.Where("reverse_property").Is("/music/artist/album")
				.Where().Id.Is("/music/album/artist")
				.Where().Type.IsAProperty;
			RunTest();
		}
		
		[Test]
		public void Example_3_4_5_5()
		{
			expected = "{\"query\":[{" +
				"\"expected_type\":null," +
				"\"unit\":null," +
				"\"id\":\"/music/track/length\"," +
				"\"type\":\"/type/property\"" +
				"}]}";
			
			query = Freebase.Query
				.Show.ExpectedType
				.Show.Unit
				.Where().Id.Is("/music/track/length")
				.Where().Type.IsAProperty;
			RunTest();
		}
		
		[Test]
		public void Example_3_5_1()
		{
			expected = "{\"query\":[{" +
				"\"type\":\"/music/artist\"," +
				"\"name\":\"The Police\"," +
				"\"album\":[{\"name\":null,\"limit\":5}]" +
				"}]}";
			
			query = Freebase.Query
				.Where().Type.Is("/music/artist")
				.Where().Name.Is("The Police")
				.Expect.Many("album").Which.Meet(
					Freebase.InnerQuery
					.Show.Name
					.LimitNumberOfResults.To( 5 )
				);
			RunTest();
		}
		
		[Test]
		public void Example_3_5_2()
		{
			expected = "{\"query\":[{" +
				"\"type\":\"/music/artist\"," +
				"\"name\":\"The Police\"," +
				"\"album\":{\"return\":\"count\"}" +
				"}]}";
			query = Freebase.Query
				.Where().Type.Is("/music/artist")
				.Where().Name.Is("The Police")
				.Expect.One("album").Which.Meets(
					Freebase.InnerQuery
					.Return.Count
				);
			RunTest();
		}
		
		[Test]
		public void Example_3_5_2_no2()
		{
			expected = "{\"query\":[{" +
				"\"name\":null," +
				"\"type\":\"/music/track\"," +
				"\"artist\":\"The Police\"," +
				"\"count\":null" +
				"}]}";
			query = Freebase.Query
				.Show.Name
				.Where().Type.Is("/music/track")
				.Where("artist").Is("The Police")
				.Count;
			RunTest();
		}
		
		[Test]
		public void Example_3_5_3()
		{
			expected = "{\"query\":[{" +
				"\"character\":null," +
				"\"type\":\"/film/performance\"," +
				"\"film\":{" +
				"\"name\":null," +
				"\"directed_by\":\"George Lucas\"" +
				"}," +
				"\"actor\":{" +
				"\"name\":null," +
				"\"/people/person/date_of_birth\":null" +
				"}," +
				"\"sort\":[\"character\"," +
				"\"film.name\"," +
				"\"-actor./people/person/date_of_birth\"]" +
				"}]}";
			
			query = Freebase.Query
				.Where().Type.Is("/film/performance")
				.Expect.One("film").Which.Meets(
					Freebase.InnerQuery
					.Show.Name
					.Where("directed_by").Is("George Lucas"))
				.Show.Property("character")
				.Expect.One("actor").Which.Meets(
					Freebase.InnerQuery
					.Show.Name
					.Show.Property("/people/person/date_of_birth"))
				.Sort.By( "character",
				         "film.name",
				         "-actor./people/person/date_of_birth" );

			RunTest();
		}
		
		[Test]
		public void Example_3_5_4()
		{
			expected = "{\"query\":[{" +
				"\"type\":\"/film/film\"," +
				"\"name\":\"Psycho\"," +
				"\"directed_by\":\"Alfred Hitchcock\"," +
				"\"starring\":[{" +
				"\"actor\":null," +
				"\"character\":null," +
				"\"index\":null," +
				"\"sort\":\"index\"," +
				"\"limit\":2" +
				"}]" +
				"}]}";
			query = Freebase.Query
				.Where().Type.Is("/film/film")
				.Where().Name.Is("Psycho")
				.Where("directed_by").Is("Alfred Hitchcock")
				.Expect.Many("starring").Which.Meet(
					Freebase.InnerQuery
					.Show.OneRecordFor.Property("actor")
					.Show.OneRecordFor.Property("character")
					.Show.OneRecordFor.Property("index")
					.Sort.ByIndex
					.LimitNumberOfResults.To( 2 )
				);
			RunTest();
		}
		
		[Test]
		public void Example_3_5_5()
		{
			expected = "{\"query\":[{" +
				"\"name\":null," +
				"\"type\":\"/music/artist\"," +
				"\"track\":\"Masters of War\"," +
				"\"album\":{" +
				"\"name\":\"Greatest Hits\"," +
				"\"optional\":\"true\"" +
				"}" +
				"}]}";
			query = Freebase.Query
				.Show.Name
				.Where().Type.Is("/music/artist")
				.Where("track").Is("Masters of War")
				.Expect.One("album").Which.Meets(
					Freebase.InnerQuery
					.Where().Name.Is("Greatest Hits")
					.Optional.True
				);
			RunTest();
		}
		
		[Test]
		public void Example_3_5_6()
		{
			expected = "{\"query\":[{" +
				"\"name\":null," +
				"\"type\":\"/music/artist\"," +
				"\"track\":\"Masters of War\"," +
				"\"album\":{" +
				"\"name\":\"Greatest Hits\"," +
				"\"optional\":\"forbidden\"" +
				"}" +
				"}]}";
			
			query = Freebase.Query
				.Show.Name
				.Where().Type.Is("/music/artist")
				.Where("track").Is("Masters of War")
				.Expect.One("album").Which.Meets(
					Freebase.InnerQuery
					.Where().Name.Is("Greatest Hits")
					.Optional.Forbidden
				);
			RunTest();
		}
		
		[Test]
		public void Example_3_6_1()
		{
			expected = "{\"query\":[{" +
				"\"type\":\"/music/album\"," +
				"\"name\":\"Synchronicity\"," +
				"\"artist\":\"The Police\"," +
				"\"track\":[{" +
				"\"name\":null," +
				"\"length\":null," +
				"\"length>\":300" +
				"}]}]}";
			query = Freebase.Query
				.Where().Type.Is("/music/album")
				.Where().Name.Is("Synchronicity")
				.Where("artist").Is("The Police")
				.Expect.Many("track").Which.Meet(
					Freebase.InnerQuery
					.Show.Name
					.Show.Property("length")
					.Where("length").IsGreaterThan(300)
				);
			RunTest();
		}
		
		[Test]
		public void Example_3_6_2()
		{
			expected = "{\"query\":[{" +
				"\"artist\":null," +
				"\"name\":null," +
				"\"length\":null," +
				"\"type\":\"/music/track\"," +
				"\"name~=\":\"love\"," +
				"\"length<\":120" +
				"}]}";
			query = Freebase.Query
				.Show.Property("artist")
				.Show.Name
				.Show.Property("length")
				.Where().Type.Is("/music/track")
				.Where().Name.Contains("love")
				.Where("length").IsLessThan(120);
			RunTest();
		}
		
		[Test]
		public void Example_3_6_3()
		{
			expected = "{\"query\":[{" +
				"\"name\":null," +
				"\"atomic_number\":null," +
				"\"type\":\"/chemistry/chemical_element\"," +
				"\"atomic_number|=\":[1,2,3]," +
				"\"sort\":\"atomic_number\"" +
				"}]}";
			query = Freebase.Query
				.Where().Type.Is("/chemistry/chemical_element")
				.Show.Name
				.Where("atomic_number").Is(1,2,3)
				.Show.Property("atomic_number")
				.Sort.By("atomic_number");
			RunTest();
		}
		
		[Test]
		public void Example_5_2()
		{
			expected = "{\"query\":{" +
				"\"id\":null," +
				"\"type\":\"/user/docs/music/note\"," +
				"\"name\":\"A\"," +
				"\"create\":\"unless_exists\"" +
				"}}";
			query = Freebase.Write
				.Show.Id
				.Where().Type.Is("/user/docs/music/note")
				.Where().Name.Is("A")
				.Create.UnlessExists;
			RunTest();
		}
		
		public void RunTest()
		{
			testWasRun = true;
			returned = SUT.FluentToMQL( query );
			Assert.AreEqual( expected, returned );
			
		}
		
		[TearDown]
		public void TearDown()
		{
			Assert.IsTrue( testWasRun, "This error was thrown if a test failed or you did not call RunTest() in the test method - please ensure RunTest() is called!" );
		}
	}
}

